#include "netwatcher.h"
#include "netwatcher.skel.h"
#include <argp.h>
#include <arpa/inet.h>
#include <bpf/bpf.h>
#include <bpf/libbpf.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include "dropreason.h"

static volatile bool exiting = false;

static char connects_file_path[1024];
static char err_file_path[1024];
static char packets_file_path[1024];
static char udp_file_path[1024];

static int sport = 0, dport = 0; // for filter
static int all_conn = 0, err_packet = 0, extra_conn_info = 0, layer_time = 0,
           http_info = 0, retrans_info = 0, udp_info = 0,net_filter = 0,drop_reason = 0,addr_to_func=0 ,icmp_info = 0 , tcp_info = 0, time_load = 0 ; // flag

static const char* tcp_states[] = {
    [1] = "ESTABLISHED", [2] = "SYN_SENT",   [3] = "SYN_RECV",
    [4] = "FIN_WAIT1",   [5] = "FIN_WAIT2",  [6] = "TIME_WAIT",
    [7] = "CLOSE",       [8] = "CLOSE_WAIT", [9] = "LAST_ACK",
    [10] = "LISTEN",     [11] = "CLOSING",   [12] = "NEW_SYN_RECV",
    [13] = "UNKNOWN",
};

static const char argp_program_doc[] = "Watch tcp/ip in network subsystem \n";

static const struct argp_option opts[] = {
    {"all", 'a', 0, 0, "set to trace CLOSED connection"},
    {"err", 'e', 0, 0, "set to trace TCP error packets"},
    {"extra", 'x', 0, 0, "set to trace extra conn info"},
    {"retrans", 'r', 0, 0, "set to trace extra retrans info"},
    {"time", 't', 0, 0, "set to trace layer time of each packet"},
    {"http", 'i', 0, 0, "set to trace http info"},
    {"sport", 's', "SPORT", 0, "trace this source port only"},
    {"dport", 'd', "DPORT", 0, "trace this destination port only"},
    {"udp", 'u', 0, 0, "trace the udp message"},
    {"net_filter",'n',0,0,"trace ipv4 packget filter "},
    {"drop_reason",'k',0,0,"trace kfree "},
    {"addr_to_func",'T',0,0,"translation addr to func and offset"},
    {"icmptime", 'I', 0, 0, "set to trace layer time of icmp"},
    {"tcpstate", 'S', 0, 0, "set to trace tcpstate"},
    {"timeload", 'L', 0, 0, "analysis time load"},

    {}};

static error_t parse_arg(int key, char *arg, struct argp_state *state) {
    char *end;
    switch (key) {
    case 'a':
        all_conn = 1;
        break;
    case 'e':
        err_packet = 1;
        break;
    case 'x':
        extra_conn_info = 1;
        break;
    case 'r':
        retrans_info = 1;
        break;
    case 't':
        layer_time = 1;
        break;
    case 'i':
        http_info = 1;
        break;
    case 's':
        sport = strtoul(arg, &end, 10);
        break;
    case 'd':
        dport = strtoul(arg, &end, 10);
        break;
    case 'u':
        udp_info = 1;
        break;
    case 'n':
        net_filter = 1;
        break;
    case 'k':
        drop_reason = 1;
        break;
    case 'T':
        addr_to_func = 1;
        break;
    case 'I':
        icmp_info = 1;
        break;
    case 'S':
        tcp_info = 1;
        break;
    case 'L':
        time_load = 1;
        break;
    default:
        return ARGP_ERR_UNKNOWN;
    }
    return 0;
}

static const struct argp argp = {
    .options = opts,
    .parser = parse_arg,
    .doc = argp_program_doc,
};

struct SymbolEntry{
    unsigned long addr;
    char name[30];
};

struct SymbolEntry symbols[300000];
int num_symbols = 0;
//定义快表
#define CACHEMAXSIZE 5
struct SymbolEntry cache[CACHEMAXSIZE];
int cache_size = 0;
//LRU算法查找函数
struct SymbolEntry find_in_cache(unsigned long int addr)
{
    // 查找地址是否在快表中
    for (int i = 0; i < cache_size; i++) {
        if (cache[i].addr == addr) {
            // 更新访问时间
            struct SymbolEntry temp = cache[i];
            // 将访问的元素移动到快表的最前面，即最近使用的位置
            for (int j = i; j > 0; j--) {
                cache[j] = cache[j - 1];
            }
            cache[0] = temp;
            return temp;
        }
    }
     // 如果地址不在快表中，则返回空
    struct SymbolEntry empty_entry;
    empty_entry.addr = 0;
    return empty_entry;
}

// 将新的符号条目加入快表
void add_to_cache(struct SymbolEntry entry) {
    // 如果快表已满，则移除最久未使用的条目
    if (cache_size == CACHEMAXSIZE) {
        for (int i = cache_size - 1; i > 0; i--) {
            cache[i] = cache[i - 1];
        }
        cache[0] = entry;
    } else {
        // 否则，直接加入快表
        for (int i = cache_size; i > 0; i--) {
            cache[i] = cache[i - 1];
        }
        cache[0] = entry;
        cache_size++;
    }
}

struct SymbolEntry findfunc(unsigned long int addr)
{
     // 先在快表中查找
    struct SymbolEntry entry = find_in_cache(addr);
    if (entry.addr != 0) {
        return entry;
    }
    unsigned long long low = 0, high = num_symbols - 1;
    unsigned long long result = -1;

    while (low <= high) {
        int mid = low + (high - low) / 2;
        if (symbols[mid].addr < addr) {
            result = mid;
            low = mid + 1;
        } else {
            high = mid - 1;
        }
    }
    add_to_cache(symbols[result]);
    return symbols[result];
};
void readallsym()
{
    FILE *file = fopen("/proc/kallsyms", "r");
    if (!file) {
        perror("Error opening file");
        exit(EXIT_FAILURE);
    }
    char line[256];
    while (fgets(line, sizeof(line), file)) {
        unsigned long addr;
        char type, name[30];
        int ret = sscanf(line, "%lx %c %s", &addr, &type, name);
        if (ret == 3) {       
            symbols[num_symbols].addr = addr;
            strncpy(symbols[num_symbols].name, name, 30);
            num_symbols++;
        }
    }

    fclose(file);
}
/*
    指数加权移动平均算法（EWMA）
    1.使用指数加权移动平均算法（EWMA）来计算每层的指数加权移动平均值，
    公式EWMA_new = alpha * new_value + (1 - alpha) * old_ewma ,alpha 指数加权系数，表示新数据点的权重，new_value 当前时延，old_ewma 旧的指数加权移动平均值
    2.根据当前时延和指数加权移动平均值*预先设定的粒度阈值（GRANULARITY）对比，来判断时延是否异常
    3.可以快速适应数据的变化，并能够有效地检测异常时延 

*/
struct LayerDelayInfo {
    float delay; // 时延数据
    int layer_index; // 层索引
};
#define GRANULARITY 3 
#define ALPHA 0.2 // 衰减因子
#define MAXTIME 10000

// 全局变量用于存储每层的移动平均值
float ewma_values[NUM_LAYERS] = {0};
int count[NUM_LAYERS] = {0};

// 指数加权移动平均算法
float calculate_ewma(float new_value, float old_ewma) { 
    return ALPHA * new_value + (1 - ALPHA) * old_ewma;
}

// 收集时延数据并检测异常
int process_delay(float layer_delay, int layer_index) {

    if(layer_delay==0) return 0;
    count[layer_index]++;
    if( ewma_values[layer_index]==0){  
        ewma_values[layer_index]=layer_delay;
        return 0;
    }
    // 计算阈值,指数加权移动平均值乘以粒度因子
    ewma_values[layer_index] = calculate_ewma(layer_delay, ewma_values[layer_index]);
    float threshold = ewma_values[layer_index] * GRANULARITY;
    if(count[layer_index]>30){
        // 判断当前时延是否超过阈值
     //   printf("%d %d:%f %f      ",layer_index,count[layer_index]++,threshold,layer_delay);
        if (layer_delay > threshold) {//异常
            return 1;
        }else{
            return 0;
        }
    }
    return 0;
}


static void sig_handler(int signo) { exiting = true; }

static void bytes_to_str(char *str, unsigned long long num) {
    if (num > 1e9) {
        sprintf(str, "%.8lfG", (double)num / 1e9);
    } else if (num > 1e6) {
        sprintf(str, "%.6lfM", (double)num / 1e6);
    } else if (num > 1e3) {
        sprintf(str, "%.3lfK", (double)num / 1e3);
    } else {
        sprintf(str, "%llu", num);
    }
}

static int print_conns(struct netwatcher_bpf *skel) {

    FILE *file = fopen(connects_file_path, "w");
    if (file == NULL) {
        fprintf(stderr, "Failed to open connects.log: (%s)\n", strerror(errno));
        return 0;
    }

    int map_fd = bpf_map__fd(skel->maps.conns_info);
    struct sock *sk = NULL;

    while (bpf_map_get_next_key(map_fd, &sk, &sk) == 0) {
        // fprintf(stdout, "next_sk: (%p)\n", sk);
        struct conn_t d = {};
        int err = bpf_map_lookup_elem(map_fd, &sk, &d);
        if (err) {
            fprintf(stderr, "Failed to read value from the conns map: (%s)\n",
                    strerror(errno));
            return 0;
        }
        char s_str[INET_ADDRSTRLEN];
        char d_str[INET_ADDRSTRLEN];

        char s_str_v6[INET6_ADDRSTRLEN];
        char d_str_v6[INET6_ADDRSTRLEN];

        char s_ip_port_str[INET6_ADDRSTRLEN + 6];
        char d_ip_port_str[INET6_ADDRSTRLEN + 6];
        if((d.saddr & 0x0000FFFF) == 0x0000007F || (d.daddr & 0x0000FFFF) == 0x0000007F)
            return 0;
        if (d.family == AF_INET) {
            sprintf(s_ip_port_str, "%s:%d",
                    inet_ntop(AF_INET, &d.saddr, s_str, sizeof(s_str)),
                    d.sport);
            sprintf(d_ip_port_str, "%s:%d",
                    inet_ntop(AF_INET, &d.daddr, d_str, sizeof(d_str)),
                    d.dport);
        } else { // AF_INET6
            sprintf(
                s_ip_port_str, "%s:%d",
                inet_ntop(AF_INET6, &d.saddr_v6, s_str_v6, sizeof(s_str_v6)),
                d.sport);
            sprintf(
                d_ip_port_str, "%s:%d",
                inet_ntop(AF_INET6, &d.daddr_v6, d_str_v6, sizeof(d_str_v6)),
                d.dport);
        }
        char received_bytes[11], acked_bytes[11];
        bytes_to_str(received_bytes, d.bytes_received);
        bytes_to_str(acked_bytes, d.bytes_acked);
        fprintf(file,
                "connection{pid=\"%d\",sock=\"%p\",src=\"%s\",dst=\"%s\","
                "is_server=\"%d\"",
                d.pid, d.sock, s_ip_port_str, d_ip_port_str, d.is_server);
        if (extra_conn_info) {
            fprintf(file,
                    ",backlog=\"%u\""
                    ",maxbacklog=\"%u\""
                    ",rwnd=\"%u\""
                    ",cwnd=\"%u\""
                    ",ssthresh=\"%u\""
                    ",sndbuf=\"%u\""
                    ",wmem_queued=\"%u\""
                    ",rx_bytes=\"%s\""
                    ",tx_bytes=\"%s\""
                    ",srtt=\"%u\""
                    ",duration=\"%llu\""
                    ",total_retrans=\"%u\"",
                    d.tcp_backlog, d.max_tcp_backlog, d.rcv_wnd, d.snd_cwnd,
                    d.snd_ssthresh, d.sndbuf, d.sk_wmem_queued, received_bytes,
                    acked_bytes, d.srtt, d.duration, d.total_retrans);
        } else {
            fprintf(file,
                    ",backlog=\"-\",maxbacklog=\"-\",cwnd=\"-\",ssthresh=\"-\","
                    "sndbuf=\"-\",wmem_queued=\"-\",rx_bytes=\"-\",tx_bytes=\"-"
                    "\",srtt=\"-\",duration=\"-\",total_retrans=\"-\"");
        }
        if (retrans_info) {
            fprintf(file, ",fast_retrans=\"%u\",timeout_retrans=\"%u\"",
                    d.fastRe, d.timeout);
        } else {
            fprintf(file, ",fast_retrans=\"-\",timeout_retrans=\"-\"");
        }
        fprintf(file, "}\n");
    }
    fflush(file);
    fclose(file);
    return 0;
}

static int print_packet(void *ctx, void *packet_info, size_t size) {
    if (udp_info || net_filter || drop_reason || icmp_info || tcp_info)
        return 0;
    const struct pack_t *pack_info = packet_info;
    if(pack_info->mac_time > MAXTIME || pack_info->ip_time > MAXTIME || pack_info->tran_time > MAXTIME)
    {
        return 0;
    }
    char d_str[INET_ADDRSTRLEN];
    char s_str[INET_ADDRSTRLEN];
    unsigned int saddr = pack_info->saddr;
    unsigned int daddr = pack_info->daddr;
    if((daddr & 0x0000FFFF) == 0x0000007F || (saddr & 0x0000FFFF) == 0x0000007F)
        return 0;
    if (dport) 
        if (pack_info->dport != dport)                             
                return 0;                                                     
                                                      
    if (sport)                                               
            if (pack_info->sport!= sport)                                  
                return 0;   

    if (pack_info->err) {
        FILE *file = fopen(err_file_path, "a");
        char reason[20];
        if (pack_info->err == 1) {
            printf("[X] invalid SEQ: sock = %p,seq= %u,ack = %u\n",
                   pack_info->sock, pack_info->seq, pack_info->ack);
            sprintf(reason, "Invalid SEQ");
        } else if (pack_info->err == 2) {
            printf("[X] invalid checksum: sock = %p\n", pack_info->sock);
            sprintf(reason, "Invalid checksum");
        } else {
            printf("UNEXPECTED packet error %d.\n", pack_info->err);
            sprintf(reason, "Unkonwn");
        }
        fprintf(file,
                "error{sock=\"%p\",seq=\"%u\",ack=\"%u\","
                "reason=\"%s\"} \n",
                pack_info->sock, pack_info->seq, pack_info->ack, reason);
        fclose(file);
    } else {
        FILE *file = fopen(packets_file_path, "a");
        char http_data[256];

        if (strstr((char *)pack_info->data, "HTTP/1")) {

            for (int i = 0; i < sizeof(pack_info->data); ++i) {
                if (pack_info->data[i] == '\r') {
                    http_data[i] = '\0';
                    break;
                }
                http_data[i] = pack_info->data[i];
            }
        } else {

            sprintf(http_data, "-");
        }
        if (layer_time) {
                   printf("%-22p %-20s %-8d %-20s %-8d %-10llu %-10llu %-10llu %-5d %-10s",
                   pack_info->sock,inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),pack_info->sport,
                   inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)),pack_info->dport,
                   pack_info->mac_time, pack_info->ip_time,
                   pack_info->tran_time, pack_info->rx, http_data);
            fprintf(
                file,
                "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\",dport=\"%d\",seq=\"%u\",ack=\"%u\","
                "mac_time=\"%llu\",ip_time=\"%llu\",tran_time=\"%llu\",http_"
                "info=\"%s\",rx=\"%d\"} \n",
                pack_info->sock,inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),pack_info->sport,
                inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)),pack_info->dport,
                pack_info->seq, pack_info->ack,
                pack_info->mac_time, pack_info->ip_time,
                pack_info->tran_time,http_data, pack_info->rx);
        } else {
                printf("%-22p %-20s %-8d %-20s %-8d %-10d %-10d %-10d %-5d %-10s",
                   pack_info->sock,inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),pack_info->sport,
                   inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)),pack_info->dport,0,0,0, pack_info->rx, http_data);
            fprintf(
                file,
                "packet{sock=\"%p\",saddr=\"%s\",sport=\"%d\",daddr=\"%s\",dport=\"%d\",seq=\"%u\",ack=\"%u\","
                "mac_time=\"%d\",ip_time=\"%d\",tran_time=\"%d\",http_"
                "info=\"%s\",rx=\"%d\"} \n",
                pack_info->sock,inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),pack_info->sport,
                inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)),pack_info->dport,
                pack_info->seq, pack_info->ack,0,0,0,http_data, pack_info->rx);
        }
        fclose(file);
    }
    if(time_load)
    {
        int mac = process_delay(pack_info->mac_time,0);
        int ip = process_delay(pack_info->ip_time,1);
        int tran = process_delay(pack_info->tran_time,2);
        if(mac||ip||tran)
        {
            printf("%-15s","abnormal data");
        }
    }
    printf("\n");
    return 0;
}
static int print_udp(void *ctx, void *packet_info, size_t size) {
    if (!udp_info)
        return 0;
    FILE *file = fopen(udp_file_path, "a+");//追加
     if (file == NULL) {
        fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno));
        return 0;
    }
    char d_str[INET_ADDRSTRLEN];
    char s_str[INET_ADDRSTRLEN];
    const struct udp_message *pack_info = packet_info;
    unsigned int saddr = pack_info->saddr;
    unsigned int daddr = pack_info->daddr;
    if(pack_info->tran_time > MAXTIME||(daddr & 0x0000FFFF) == 0x0000007F || (saddr & 0x0000FFFF) == 0x0000007F)
        return 0;
    
    printf("%-20s %-20s %-20u %-20u %-20llu %-20d %-20d",
           inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),
           inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport,
           pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len);
    fprintf(
            file,
            "packet{saddr=\"%s\",daddr=\"%s\",sport=\"%u\","
            "dport=\"%u\",udp_time=\"%llu\",rx=\"%d\",len=\"%d\"} \n",
            inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),
            inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport,
            pack_info->dport, pack_info->tran_time,pack_info->rx,pack_info->len);
    fclose(file);
    if(time_load)
    {
        int flag = process_delay(pack_info->tran_time, 3);
        if(flag)
            printf("%-15s","abnormal data");
        
    }
    printf("\n");

    return 0;
}

static int print_netfilter(void *ctx, void *packet_info, size_t size) {
    if(!net_filter)
        return 0;
    char d_str[INET_ADDRSTRLEN];
    char s_str[INET_ADDRSTRLEN]; 
    const struct netfilter *pack_info = packet_info;
    if(pack_info->local_input_time > MAXTIME || pack_info->forward_time > MAXTIME || pack_info->local_out_time > MAXTIME || pack_info->post_routing_time > MAXTIME ||pack_info->pre_routing_time >MAXTIME )
        return 0;
    unsigned int saddr = pack_info->saddr;
    unsigned int daddr = pack_info->daddr;
    if((daddr & 0x0000FFFF) == 0x0000007F || (saddr & 0x0000FFFF) == 0x0000007F)
        return 0;
    printf("%-20s %-20s %-12d %-12d %-8lld %-8lld% -8lld %-8lld %-8lld %-8d",
            inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),
            inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)),
            pack_info->sport,pack_info->dport,
            pack_info->pre_routing_time,
            pack_info->local_input_time,
            pack_info->forward_time,
            pack_info->post_routing_time,
            pack_info->local_out_time,
            pack_info->rx);
    //定义一个数组用于存储需要检测的时延数据和对应的层索引
    struct LayerDelayInfo layer_delay_infos[] = {
            {pack_info->pre_routing_time, 4},
            {pack_info->local_input_time, 5},
            {pack_info->forward_time, 6},
            {pack_info->post_routing_time, 7},
            {pack_info->local_out_time, 8}
     };
    if(time_load)
    {
        // 循环遍历数组
        for (int i = 0; i < 5; i++) {
            //数组的总字节数除以第一个元素的字节数得到元素的个数
            float delay = layer_delay_infos[i].delay;
            int layer_net = layer_delay_infos[i].layer_index;
            int flag = process_delay(delay, layer_net);
            if (flag) 
                printf("%-15s","abnormal data");
        }
    }
    printf("\n");

    return 0;
}

static int print_tcpstate(void *ctx, void *packet_info, size_t size) {
    if(!tcp_info)
        return 0;
    char d_str[INET_ADDRSTRLEN];
    char s_str[INET_ADDRSTRLEN]; 
    const struct tcp_state *pack_info = packet_info;
    unsigned int saddr = pack_info->saddr;
    unsigned int daddr = pack_info->daddr;
    printf("%-20s %-20s %-20d %-20d %-20s %-20s  %-20lld\n",
            inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),
            inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)),
            pack_info->sport,pack_info->dport,tcp_states[pack_info->oldstate],tcp_states[pack_info->newstate],pack_info->time);

    return 0;
}

static int print_kfree(void *ctx, void *packet_info, size_t size) {
    if(!drop_reason)
        return 0;
    char d_str[INET_ADDRSTRLEN];
    char s_str[INET_ADDRSTRLEN]; 
    const struct reasonissue *pack_info = packet_info;
    unsigned int saddr = pack_info->saddr;
    unsigned int daddr = pack_info->daddr;
    if(saddr == 0 && daddr ==0 )
    {
        return 0;
    }
    char prot[6];
    if(pack_info->protocol==2048)
    {
        strcpy(prot, "ipv4");
    }
    else if(pack_info->protocol==34525)
    {
        strcpy(prot, "ipv6");
    }
    else {
        // 其他协议
        strcpy(prot, "other");
    }
    time_t now = time(NULL);
    struct tm *localTime = localtime(&now);
    printf("%02d:%02d:%02d      %-17s %-17s %-10u %-10u %-10s", 
    localTime->tm_hour, localTime->tm_min, localTime->tm_sec,
    inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),
    inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)), pack_info->sport,pack_info->dport,prot);
    if(!addr_to_func)
        printf("%-34lx",pack_info->location);
    else {
        struct SymbolEntry data= findfunc(pack_info->location);
        char result[40];
        sprintf(result, "%s+0x%lx", data.name, pack_info->location - data.addr);
        printf("%-34s",result);
    }
    printf("%s\n", SKB_Drop_Reason_Strings[pack_info->drop_reason]);
    return 0;
}

static int print_icmptime(void *ctx, void *packet_info, size_t size) {
    if(!icmp_info)
        return 0;
    char d_str[INET_ADDRSTRLEN];
    char s_str[INET_ADDRSTRLEN]; 
    const struct icmptime *pack_info = packet_info;
    if(pack_info->icmp_tran_time > MAXTIME)
    {
        return 0;
    }
    unsigned int saddr = pack_info->saddr;
    unsigned int daddr = pack_info->daddr;
    printf("%-20s %-20s %-12lld %-12d",
            inet_ntop(AF_INET, &saddr, s_str, sizeof(s_str)),
            inet_ntop(AF_INET, &daddr, d_str, sizeof(d_str)),
            pack_info->icmp_tran_time,
            pack_info->flag);    
    if(time_load)
    {
        int icmp_data = process_delay(pack_info->icmp_tran_time, 9);
        if(icmp_data)
        {
            printf("%-15s\n","abnormal data");
        }
    }
    printf("\n");
    return 0;
}

int main(int argc, char **argv) {
    char *last_slash = strrchr(argv[0], '/');
    if (last_slash) {
        *(last_slash + 1) = '\0';
    }
    strcpy(connects_file_path, argv[0]);
    strcpy(err_file_path, argv[0]);
    strcpy(packets_file_path, argv[0]);
    strcpy(udp_file_path, argv[0]);
    strcat(connects_file_path, "data/connects.log");
    strcat(err_file_path, "data/err.log");
    strcat(packets_file_path, "data/packets.log");
    strcat(udp_file_path,"data/udp.log");
    struct ring_buffer *rb = NULL;
    struct ring_buffer *udp_rb = NULL;
    struct ring_buffer *netfilter_rb = NULL;
    struct ring_buffer *kfree_rb = NULL;
    struct ring_buffer *icmp_rb = NULL;
    struct ring_buffer *tcp_rb = NULL;
    struct netwatcher_bpf *skel;
    int err;
    /* Parse command line arguments */
    if (argc > 1) {
        err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
        if (err)
            return err;
    }

    /* Cleaner handling of Ctrl-C */
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);

    /* Open load and verify BPF application */
    skel = netwatcher_bpf__open();
    if (!skel) {
        fprintf(stderr, "Failed to open BPF skeleton\n");
        return 1;
    }

    /* Parameterize BPF code */
    skel->rodata->filter_dport = dport;
    skel->rodata->filter_sport = sport;
    skel->rodata->all_conn = all_conn;
    skel->rodata->err_packet = err_packet;
    skel->rodata->extra_conn_info = extra_conn_info;
    skel->rodata->layer_time = layer_time;
    skel->rodata->http_info = http_info;
    skel->rodata->retrans_info = retrans_info;
    skel->rodata->udp_info = udp_info;
    skel->rodata->net_filter = net_filter;
    skel->rodata->drop_reason = drop_reason;
    skel->rodata->tcp_info = tcp_info;
    skel->rodata->icmp_info = icmp_info;

    if(addr_to_func)
        readallsym();

    err = netwatcher_bpf__load(skel);
    if (err) {
        fprintf(stderr, "Failed to load and verify BPF skeleton\n");
        goto cleanup;
    }

    /* Attach tracepoint handler */
    err = netwatcher_bpf__attach(skel);
    if (err) {
        fprintf(stderr, "Failed to attach BPF skeleton\n");
        goto cleanup;
    }
    if (udp_info) {
        printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s\n", "saddr", "daddr", "sprot",
               "dprot", "udp_time","rx","len");
    }
    else if(net_filter)
    {
        printf("%-20s %-20s %-12s %-12s %-8s %-8s %-7s %-8s %-8s %-8s\n", "saddr", "daddr","dprot", "sprot",
            "PreRT","L_IN","FW","PostRT","L_OUT","rx");
    }
    else if(drop_reason)
    {
        printf("%-13s %-17s %-17s %-10s %-10s %-9s %-33s %-30s\n", "time","saddr", "daddr","sprot", "dprot","prot","addr","reason");
    }
    else if(icmp_info)
    {
        printf("%-20s %-20s %-12s %-12s\n", "saddr", "daddr","time","flag");
    }
    else if(tcp_info)
    {
        printf("%-20s %-20s %-20s %-20s %-20s %-20s %-20s \n", "saddr", "daddr","sport","dport","oldstate","newstate","time");
    }
    else{
       printf("%-22s %-20s %-8s %-20s %-8s %-10s %-10s %-10s %-5s %-10s \n", "SOCK","Saddr","Sport","Daddr","Dport", 
            "MAC_TIME", "IP_TIME", "TRAN_TIME", "RX", "HTTP");
    }
    udp_rb =ring_buffer__new(bpf_map__fd(skel->maps.udp_rb), print_udp, NULL, NULL);
    if (!udp_rb) {
        err = -1;
        fprintf(stderr, "Failed to create ring buffer(udp)\n");
        goto cleanup;
    }
    netfilter_rb =ring_buffer__new(bpf_map__fd(skel->maps.netfilter_rb), print_netfilter, NULL, NULL);
    if (!netfilter_rb) {
        err = -1;
        fprintf(stderr, "Failed to create ring buffer(netfilter)\n");
        goto cleanup;
    }
    kfree_rb =ring_buffer__new(bpf_map__fd(skel->maps.kfree_rb), print_kfree, NULL, NULL);
    if (!kfree_rb) {
        err = -1;
        fprintf(stderr, "Failed to create ring buffer(kfree)\n");
        goto cleanup;
    }
    icmp_rb =ring_buffer__new(bpf_map__fd(skel->maps.icmp_rb), print_icmptime, NULL, NULL);
    if (!icmp_rb) {
        err = -1;
        fprintf(stderr, "Failed to create ring buffer(icmp)\n");
        goto cleanup;
    }
    tcp_rb =ring_buffer__new(bpf_map__fd(skel->maps.tcp_rb), print_tcpstate, NULL, NULL);
    if (!tcp_rb) {
        err = -1;
        fprintf(stderr, "Failed to create ring buffer(tcp)\n");
        goto cleanup;
    }
    /* Set up ring buffer polling */
    rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), print_packet, NULL, NULL);
    if (!rb) {
        err = -1;
        fprintf(stderr, "Failed to create ring buffer(packet)\n");
        goto cleanup;
    }
    FILE *err_file = fopen(err_file_path, "w+");
    if (err_file == NULL) {
        fprintf(stderr, "Failed to open err.log: (%s)\n", strerror(errno));
        return 0;
    }
    fclose(err_file);
    FILE *packet_file = fopen(packets_file_path, "w+");
    if (packet_file == NULL) {
        fprintf(stderr, "Failed to open packets.log: (%s)\n", strerror(errno));
        return 0;
    }
    fclose(packet_file);
    FILE *udp_file = fopen(udp_file_path, "w+");
    if (udp_file == NULL) {
        fprintf(stderr, "Failed to open udp.log: (%s)\n", strerror(errno));
        return 0;
    }
    fclose(udp_file);

    /* Process events */
    while (!exiting) {
        err = ring_buffer__poll(rb, 100 /* timeout, ms */);
        err = ring_buffer__poll(udp_rb, 100 /* timeout, ms */);
        err = ring_buffer__poll(netfilter_rb, 100 /* timeout, ms */);
        err = ring_buffer__poll(kfree_rb, 100 /* timeout, ms */);
        err = ring_buffer__poll(icmp_rb, 100 /* timeout, ms */);
        err = ring_buffer__poll(tcp_rb, 100 /* timeout, ms */);
        print_conns(skel);
        sleep(1);
        /* Ctrl-C will cause -EINTR */
        if (err == -EINTR) {
            err = 0;
            break;
        }
        if (err < 0) {
            printf("Error polling perf buffer: %d\n", err);
            break;
        }
    }

cleanup:
    netwatcher_bpf__destroy(skel);
    return err < 0 ? -err : 0;
}